﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Threading;

namespace System.Windows.Media.DirectShow.Filters
{
    public class WaveWriterOutputPin : BaseOutputPin
    {
        private AMMediaType[] _mediaTypes = new AMMediaType[] { new AMMediaType {
            MajorType = MediaMajorTypes.Stream,
            SubType = MediaSubTypes.None,
        }};

        private bool headerWrited = false;
        private int chunkSize;
        private WaveWriter filter;

        public WaveWriterOutputPin(WaveWriter filter)
            : base(filter)
        {
            this.filter = filter;
        }


        public override void Run(TimeSpan start)
        {
            base.Run(start);

            // Restart to write
            chunkSize = 0;
            headerWrited = false;
        }

        public override void Inactive()
        {
            // Close the header
            WriteHeader();

            base.Inactive();
        }

        public override void ProcessSample(IMediaSample sample)
        {
            base.ProcessSample(sample);

            WriteHeader();
        }

        protected internal override void DeliverAsync(IMediaSample sample)
        {
            base.DeliverAsync(sample);

            Interlocked.Add(ref chunkSize, (int)sample.Length);
        }

        public override void DeliverEndOfStream()
        {
            WriteHeader();

            base.DeliverEndOfStream();
        }

        private void WriteHeader()
        {
            if (this.ConnectedPin == null) return;
            headerWrited = true;

            using (MemoryStream ms = new MemoryStream())
            {
                BinaryWriter writer = new BinaryWriter(ms);

                WaveFormatExtensible format = (WaveFormatExtensible)this.filter.InputPin.ConnectedMediaType.Format;
                writer.Write(0x46464952); // "RIFF" file description header
                writer.Write(chunkSize + WaveWriter.WaveHeaderSize); // file size
                writer.Write(0x45564157); // "WAVE" description header
                writer.Write(0x20746D66); // "fmt " description header
                writer.Write(16);

                writer.Write(format.FormatTag); // WAVE type format
                writer.Write(format.Channels);
                writer.Write(format.SamplesPerSec);
                writer.Write(format.AverageBytesPerSecond);
                writer.Write(format.BlockAlign);
                writer.Write(format.BitsPerSample);
                writer.Write(0x61746164); // "data" description header
                writer.Write(chunkSize); // size of data chunk
                writer.Flush();

                ms.Position = 0;

                IMediaSample sample = this.Allocator.GetBuffer(TimeSpan.Zero, TimeSpan.Zero);
                sample.StartMediaTime = 0;
                sample.EndMediaTime = ms.Length;
                sample.Length = ms.Read(sample.Data, 0, (int)sample.Length);
                this.DeliverAsync(sample);
            }
        }

        public override System.Collections.Generic.IEnumerable<AMMediaType> MediaTypes
        {
            get { return _mediaTypes; }
        }

        public override string Name
        {
            get { return "Output stream"; }
        }
    }
}
